/** \file
 * @brief MSP430/IAR port of the RTOS
 * 
 * @page msp430iar_port  HAL for MSP430 family with  IAR compiler
 * \section msp430iar_port_section HAL for MSP430 family with IAR compiler
 *
 * This is the TiROS hardware abstraction layer for the Texas
 * Instruments MSP430x family of embedded microcontrollers with the
 * IAR compiler. This
 * microcontroller family can support software interrupts. These
 * microcontrollers can be driven from a 32kHz crystal oscillator.
 * The time functions have been written assuming such an oscillator
 * drives the timer. If this is not the case, modify the functions in porttime.h
 * and the timer initialization functions in tr_port.c.
 *
 * Note that in the MSP430 architecture, the low-power state of the
 * processor is stored in the status register.  Since this is part of
 * the context, per-task power control is automatic.  To keep the
 * processor in low-power state during idle time, simple define
 * the idle task as
 * \code
 * void idle_task(void *dummy) 
 * {
 *   while(1) {
 *     // Set low power state here 
 *   }
 * }
 * \endcode
 * 
 * Tested with TI MSP430x1611
 *  To use the IAR tools for development:
 *  Set the following directories in the include path for project
 *  options ( C compiler->Preprocessor)
 * <OL>
 * <LI> $PROJ_DIR$   (containing your proj_config.h file) </LI>
 * <LI> path to  the  inc directory </LI>
 * <LI> path to src/tiros </LI>
 * <LI> path to src/tiros/port/msp430_iar </LI>
 * </OL> 
 *
 * <H2> Configuration Options: </H2>
 * <DL>
 *    <DT>  TRPORT_MSP_OSTIMER [A | B] : <DD> Options are A or B.  The MSP430 family
 * supports two timers, TimerA and TimerB.  TimerA is available on the
 * entire family line. TimerB is only available on some.  By default,
 * if this configuration option is not set, TimerA is used as source
 * for the timer interrupt.
 * <H2> Writing ISRs. </H2>
 *   If no TiROS API functions will be called, interrupts will stay
 *  disabled, then the ISR can be
 *  written very simply. See the requirements in \ref isr.
 * \code
 * #pragma vector=VECTOR_NUM
 *  __interrupt void ISR_function(void)
 * {
 *  // do stuff 
 * }
 * \endcode
 *
 * If TiROS APIs will be used, write the ISR as follows.
 * \code
 * #include "tr_int.h"
 * #pragma vector=VECTOR_NUM
 * __raw __interrupt void ISR_function(void) 
 * {
 *   HALINT_ISR_ENTRY();
 *   int x;
 *   OS_CRITICAL_ENABLE();   // Only needed if any critical sections
 *                           // will be used and interrupt nesting is enabled.
 *
 *   OS_ISR_BEGIN();         // Mark the beginning of an ISR. Note
 *                           // that if OS_CRITICAL_ENABLE() is also
 *                           // present, then this comes after 
 *                           // that. 
 *  
 *    x = 3;
 *    Do more stuff ....
 *  
 *  OS_ISR_END();
 *  HALINT_ISR_EXIT();
 * }
 * \endcode
 * 
 *
 */

/* Author: Ratish J. Punnoose, 2006
 * This file is part of TiROS, the Tickless Real-Time Operating System.
 * Copyright(c) 2006, 2007: Ratish J. Punnoose.
 * Copyright(c) 2006 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
 * certain rights in this software. 
 * 
 * TiROS is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 or (at your option) any later version.
 *
 * TiROS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with TiROS; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * As a special exception, if other files instantiate templates or use macros
 * or inline functions from this file, or you compile this file and link it
 * with other works to produce a work based on this file, this file does not
 * by itself cause the resulting work to be covered by the GNU General Public
 * License. However the source code for this file must still be made available
 * in accordance with section (3) of the GNU General Public License.
 *
 * This exception does not invalidate any other reasons why a work based on
 * this file might be covered by the GNU General Public License.
 *
 */


#ifndef __MSP430IAR_TIROS_PORT_H
#define __MSP430IAR_TIROS_PORT_H
/*@ignore@*/
#define TIROS_USER_CONFIG
#  include "proj_config.h"
#undef TIROS_USER_CONFIG
/*@end@*/

#include <msp430.h>
#include <intrinsics.h>
#include <stdint.h>


/* The data type used by this port for subtime_t */
#define subtime_t uint16_t
#define LT_INLINE static inline 



#include <tiros/tr_time.h>
/* Insert your port for time functions for your hardware */
#include "porttime.h"

/* Define the value of infinite time for this port */
#define TRPORT_INF_TIME {~0 , ~0 }



/* The msp port can use registers for passing return values to
    blocked tasks */
#define TIROS_REGISTER_PASSING



/* Define basic types for   the hardware word, boolean, and pointer */
typedef uintptr_t osword_t;
typedef void*     osptr_t;
typedef osword_t*  osstkptr_t;
typedef void (* osfnptr_t)(void *fnarg);
typedef uintptr_t  osptrword_t;

#define ILLEGAL_STACK ((osstkptr_t) ~0)
#define TR_ILLEGAL_ADDR  ((osptr_t)    ~0)



/* Port specific function implemented in tr_port.c */

/* \name Context_switching_functions
 * @{ */

/* Initialize the stack for a task.
 * @param stk  Pointer to the stack.
 * @param stacksize  Size of the stack.
 * @param PC   Program counter.
 * @param init_arg   Initial argument passed to the task */
/*@shared@*/
osstkptr_t hal_stk_init(osword_t *stk, osword_t stacksize, osfnptr_t pc,
			osptr_t init_arg); 


#ifdef TIROS_REGISTER_PASSING
/* Set a return value for a task whose context is stored.
 *
 * This injects a return value into the context of a task that is currently frozen.
 * @param stackptr  Pointer to the stack.  The stack should also contain the context.
 * @param retval    The return value to be injected into the task context */
static inline void hal_retval_set(osstkptr_t stackptr, 
				  /*@null@*/ osptr_t retval);
static inline void hal_retval_set(osstkptr_t stackptr, 
				  /*@null@*/ osptr_t retval)
{
	/* IAR passes the return value in register 12 */
	*(stackptr + 3) = (osword_t)retval;
}
#endif



void hal_init(void);


void hal_time_get(/*@out@*/ trtime_t *t);
int hal_time_set(const trtime_t *t);



/* Set an alarm.
 *  When the alarm is reached, osint_alarm_reached is called
 * @param lt Pointer to time */
void  hal_alarm_set(/*@null@*/ const trtime_t *lt);






/* Enable critical section calls in the the called function
 *
 *  This has to be called at the beginning of the function before
 *  calling OS_CRITICAL_BEGIN or OS_CRITICAL_END.  This should be the
 *  first call after variable declaration.  These section should not
 *  be added within an ISR if Interrupt nesting is turned on, because
 *  the interrupt nesting adds this in automatically, and duplication
 *  will result in compiler complaints. */
#define OS_PORT_CRITICAL_ENABLE()   /*@unused@*/ istate_t osint_int_status
#define OS_PORT_ISR_BEGIN()                     		\
	extern uint8_t os_isr_nesting;          		\
	os_isr_nesting++;





/* Begin a critical section 
 * 
 * The current interrupt state is saved in osint_int_status and then
 * interrupts are disabled */
#define OS_PORT_CRITICAL_BEGIN() \
    osint_int_status = __get_interrupt_state(); \
    __disable_interrupt()


/* End a critical section
 *
 * The saved interrupt state is restored */
#define OS_PORT_CRITICAL_END()  \
    __set_interrupt_state(osint_int_status);

/* Enable interrupts
 * This allows the user to explicitly enable interrupts */
#define OS_PORT_INT_ENABLE()      __enable_interrupt()

/* Disable interrupts
 * This allows the user to explicitly disable interrupts */
#define OS_PORT_INT_DISABLE()     __disable_interrupt()




/* Context switch from user level (non-ISR)
 *  This function is called from within an OS call, to switch the
 *  running process.  It also provides a return value. This is a
 *  pseudo return-value.  This function does not actually determine
 *  the value returned.  The return value is set by hal_retval_set
 *  which is often invoked when waking the process up from a blocked
 *  state. 
 * In this msp iar port, the hal_ctxt_switch has the __task attribute to
 * prevent iar from adding miscellaneous header code to the
 * function. 
 *    
 * @return  Return value.  */


__task  osstkptr_t hal_ctxt_switch(void);

/* Initially 14 words are used to store MSP430 register intialization,
 * R0 - R15 , without R1, R3*/
#define MSP430_INIT_STACK_OCCUPANCY      14

#define TRPORT_MIN_CTXT_SZ   (MSP430_INIT_STACK_OCCUPANCY + 1 )


__task  void hal_ctxt_load(osstkptr_t ctxt_ptr);





/* @} End context switching functions  */	







#ifndef TRPORT_RESPONSE_TIME
#define PORT_WAKEUP_RESPONSE_SUBUNITS  40
#define TRPORT_RESPONSE_TIME  { 0, PORT_WAKEUP_RESPONSE_SUBUNITS }
#endif






#ifndef TRPORT_MSP_OSTIMER
/* Which timer on the MSP430 should be used for servicing the ticks.
 * For a default use TIMER A since it is available on all the MSP430x
 * family. */
#define TRPORT_MSP_OSTIMER  A
#endif








extern int halint_kernel_trap_flagged;


/*  Force a software interrupt */
static inline void OS_KERNEL_TRAP(void)  ;
static inline void OS_KERNEL_TRAP(void)  
{ halint_kernel_trap_flagged = 1;}

/* According to the IAR 430 Compiler Reference Guide, Pg 99, the
 * inline assembly does not work with automatic variables */
extern  osstkptr_t kernel_trap_ctxt_ptr;

#define HALINT_ISR_ENTRY()    \
        asm ( "push   r4    \n\t"	\
	      "push   r5    \n\t"	\
	      "push   r6    \n\t"	\
	      "push   r7    \n\t"	\
	      "push   r8    \n\t"	\
	      "push   r9    \n\t"	\
	      "push   r10   \n\t"	\
	      "push   r11   \n\t"	\
	      "push   r12   \n\t"	\
	      "push   r13   \n\t"	\
	      "push   r14   \n\t"	\
	      "push   r15   \n\t"				\
	      "mov.w  r1, &kernel_trap_ctxt_ptr \n\t"	);	\
	halint_kernel_trap_flagged = 0;

#define HALINT_ISR_EXIT()  \
	if (halint_kernel_trap_flagged)  \
		kernel_trap_ctxt_ptr = osint_taskswitcher(kernel_trap_ctxt_ptr); \
	asm ( "mov.w &kernel_trap_ctxt_ptr, r1 \n"    \
	      "pop	r15	\n"                   \
	      "pop	r14	\n"                   \
	      "pop	r13	\n"                   \
	      "pop	r12	\n"                   \
	      "pop	r11	\n"                   \
	      "pop	r10	\n"                   \
	      "pop	r9	\n"                   \
	      "pop	r8	\n"                   \
	      "pop	r7	\n"                   \
	      "pop	r6	\n"                   \
	      "pop	r5	\n"                   \
	      "pop	r4	\n"                   \
	      "reti		\n"                   \
		);

	


#ifdef TIROSINT_DATA_DEBUG
/* Compute the program counter, from the stored context (stack
 * pointer)
 * This function is used for debugging.  Implement this by extracting
 * the program counter out of the stack pointer if possible.
 *
 * @param ctxt_ptr  The context of the current task
 * @return The program counter */
osptrword_t hal_pc_from_ctxt(osstkptr_t ctxt_ptr);




#ifdef TIROS_STK_CHECK
/* Return the maximum stack occupancy of the task.
 * This hal implemented function should provide the 
 * maximum stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr        Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The maximum stack occupancy */
osword_t hal_stkusage_max(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			  osword_t stksize);

/* Return the current stack occupancy of the task.
 * This hal implemented function should provide the 
 * current stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr   Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The current stack occupancy */
osword_t hal_stkusage_curr(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			   osword_t stksize);
#endif /*TIROS_STK_CHECK */

#endif






/* End of tr_port.h  ======================================== */
#endif   
